package android.Dominion.GameBLL.Rules;

import java.util.HashMap;
import java.util.List;

import android.Dominion.GameBLL.Player;
import android.Dominion.GameBLL.Cards.Card;
import android.Dominion.GameBLL.Cards.CardPile;
import android.Dominion.GameBLL.Cards.CardType;
import android.Dominion.Graph.ChoiceNode;
import android.Dominion.Graph.ChoiceType;
import android.Dominion.Graph.GraphManager;
import android.Dominion.Graph.GraphNode;
import android.Dominion.Graph.GraphNodeFactory;
import android.Dominion.Graph.GraphNodeType;
import android.Dominion.Graph.MessageNode;
import android.Dominion.Graph.MessageType;
import android.Dominion.Graph.WaitNode;

public class RuleSet
{

	// things that Action Cards can do...
	public void PutCardIntoLocation(Card card, Player player, Location loc)
	{
		// Puts the card into the Location belonging to the player
		// Player is ignored (or NONE) for Location == (Supply|Trash)
	}

	// shortcut method
	// puts the top cards of the players deck into their hand
	public void DrawCards(Player player, int count)
	{
		List<Card> cardToDrawList = SelectCards(player, Operation.EqualTo, count, player, Location.DeckTop, Operation.None, 0, CardType.Any);
		PutCardIntoLocation(cardToDrawList.get(0), player, Location.Hand);
	}

	// shortcut method
	public Player SelectSelfPlayer()
	{
		List<Player> selfList = SelectPlayersFromGroup(PlayerGroup.Self);
		Player self = selfList.get(0);
		return self;
	}

	public List<Player> SelectPlayersFromGroup(PlayerGroup group)
	{
		// user picks any player from the playerGroup
		// if playerGroup == Self, then Self is automatically returned
		return null;
	}

	public Location SelectLocation(Player player)
	{
		// user picks any location belonging to the player
		return null;
	}

	// shortcut method
	public Card SelectOneCard(Player byPlayer, Player fromPlayer, Location fromLoc, Operation costOp, int Cost, CardType type)
	{
		List<Card> cardList = SelectCards(byPlayer, Operation.EqualTo, 1, fromPlayer, fromLoc, costOp, Cost, type);
		return cardList.get(0);
	}

	public List<Card> SelectCards(Player byPlayer, Operation countOp, int count, Player fromPlayer, Location fromLoc, Operation costOp, int Cost, CardType type)
	{
		// 'byPlayer' picks 'count' cards from the 'player's 'location' of the
		// 'type'
		// with a cost [<|<=|==|>=|>] 'cost'

		// countOp == None means any number of cards. //maybe?
		// Player is ignored (or NONE) for Location == (Supply|Trash)
		// Cost is ignored for costOp == None
		return null;
	}

	public List<Card> GetCards(int count, Player fromPlayer, Location fromLoc)
	{
		// Returns 'count' cards from the 'player's 'location'.
		// No player choice is made in this function, nor are the card moved
		// anywhere
		return null;
	}

	public void GainCard(Card card)
	{

	}

	public void AddAction(Player player, int count)
	{

	}

	public void AddMoney(Player player, int count)
	{

	}

	public void AddBuy(Player player, int count)
	{

	}

	// how to make attack checks happen asynchronously??
	// Check to see if this player is attackable,
	// aka, if they have chosen to play a Moat
	public boolean CanAttack(Player player)
	{
		return true;
	}

	public void RevealCards(Player byPlayer, List<Card> cardList)
	{

	}

	public void RevealCards(List<Card> cardList)
	{

	}

	public List<RuleChoice> Choose(List<RuleChoice> options, int numToChoose)
	{
		return null;
	}

	public void Mine()
	{

		Player self = SelectSelfPlayer();

		Card selectedCard = SelectOneCard(self, self, Location.Hand, Operation.None, 0, CardType.Any);
		PutCardIntoLocation(selectedCard, Player.NONE, Location.Trash);
		Card gainedCard = SelectOneCard(self, Player.NONE, Location.Supply, Operation.LessThanEqualTo, selectedCard.getCost() + 3, CardType.Treasure);

		// PutCardIntoLocation(gainedCard, self, Location.Discard);
		PutCardIntoLocation(gainedCard, self, Location.Hand);
	}

	public void Remodel()
	{
		Player self = SelectSelfPlayer();
		Card selectedCard = SelectOneCard(self, self, Location.Hand, Operation.None, 0, CardType.Any);
		PutCardIntoLocation(selectedCard, Player.NONE, Location.Trash);

		int maxCost = selectedCard.getCost() + 2;
		Card gainedCard = SelectOneCard(self, Player.NONE, Location.Supply, Operation.LessThanEqualTo, maxCost, CardType.Any);
		PutCardIntoLocation(gainedCard, self, Location.Discard);
	}

	public void Workshop()
	{
		Player self = SelectSelfPlayer();

		Card gainedCard = SelectOneCard(self, Player.NONE, Location.Supply, Operation.LessThanEqualTo, 4, CardType.Any);
		PutCardIntoLocation(gainedCard, self, Location.Discard);
	}

	public void Cellar()
	{
		Player self = SelectSelfPlayer();
		int handCount = self.getArea().getHand().size();
		AddAction(self, 1);
		List<Card> selectedCardList = SelectCards(self, Operation.LessThanEqualTo, handCount, self, Location.Hand, Operation.None, 0, CardType.Any);
		for (Card card : selectedCardList)
		{
			PutCardIntoLocation(card, self, Location.Discard);
			DrawCards(self, 1);
		}
	}

	public void Village()
	{
		Player self = SelectSelfPlayer();
		DrawCards(self, 1);
		AddAction(self, 2);
	}

	public void Smithy()
	{
		Player self = SelectSelfPlayer();
		List<Card> cardToDrawList = SelectCards(self, Operation.EqualTo, 3, self, Location.DeckTop, Operation.None, 0, CardType.Any);
		PutCardIntoLocation(cardToDrawList.get(0), self, Location.Hand);
	}

	public void Market()
	{
		Player self = SelectSelfPlayer();
		List<Card> cardToDrawList = SelectCards(self, Operation.EqualTo, 3, self, Location.DeckTop, Operation.None, 0, CardType.Any);
		PutCardIntoLocation(cardToDrawList.get(0), self, Location.Hand);
		AddAction(self, 1);
		AddBuy(self, 1);
		AddMoney(self, 1);

	}

	public void Wookcutter()
	{
		Player self = SelectSelfPlayer();
		AddBuy(self, 1);
		AddMoney(self, 2);
	}

	// How to make attacks happen asynchronously?
	public void Militia()
	{
		Player self = SelectSelfPlayer();
		AddMoney(self, 2);

		List<Player> others = SelectPlayersFromGroup(PlayerGroup.Other);
		for (Player player : others)
		{
			if (CanAttack(player))
			{
				int numToDiscard = player.getArea().getHand().size() - 3;
				if (numToDiscard > 0)
				{
					List<Card> discardList = SelectCards(player, Operation.EqualTo, numToDiscard, player, Location.Hand, Operation.None, 0, CardType.Any);
					for (Card card : discardList)
					{
						PutCardIntoLocation(card, player, Location.Discard);
					}
				}
			}
		}

	}

	public void Thief(int cardId)
	{
		GraphManager gMgr = new GraphManager();
		GraphNodeFactory nodeFac = new GraphNodeFactory();
		GraphNode currNode = gMgr.getCurrentNode();

		Player currPlayer = SelectPlayersFromGroup(PlayerGroup.Self).get(0);
		List<Player> others = SelectPlayersFromGroup(PlayerGroup.Other);
		GraphNode thiefMainNode = nodeFac.MakeGraphNode(GraphNodeType.Default);

		thiefMainNode.setDescr(currPlayer.getName() + "plays a Thief");

		// since the server already knows who has reaction cards, send a
		// choice to each player with reaction cards seeing if they want to use
		// one
		WaitNode waitForAttackReaction = (WaitNode) nodeFac.MakeGraphNode(GraphNodeType.WaitNode);
		for (Player p : others)
		{
			// check for reaction cards
			List<Card> reactionCards = GetReactionCards(p);
			if (reactionCards != null && reactionCards.size() > 0)
			{
				// send choice if they want to react (reveal moat, reveal secret
				// chambers and do actions)
				ChoiceNode reaction = (ChoiceNode)nodeFac.MakeGraphNode(GraphNodeType.ChoiceNode));
				reaction.setDescr(p.getName() + " may play one of their reactions");
				reaction.setDisplayText("Choose reaction(s) to reveal");
				reaction.setType(ChoiceType.SelectCards);
				reaction.setMinSelect(0);
				reaction.setMaxSelect(reactionCards.size());
				
				
			}

		}

		// send a attack message to each player,
		// giving them time to select a reaction card before starting attack
		// resolution
		// WaitNode waitForAttackReaction = (WaitNode)
		// nodeFac.MakeGraphNode(GraphNodeType.WaitNode);
		for (Player p : others)
		{
			MessageNode atkMessage = (MessageNode) nodeFac.MakeGraphNode(GraphNodeType.MessageNode);
			atkMessage.setType(MessageType.AttackPlayed);
			atkMessage.setFrom(currPlayer);
			atkMessage.setTo(p);
			atkMessage.setDescr(currPlayer.getName() + " attacks " + p.getName());

			atkMessage.setResponseRequired(true);
			atkMessage.setRespondToNode(waitForAttackReaction.getId());
			waitForAttackReaction.AddWaitFor(atkMessage);

			thiefMainNode.AddChild(atkMessage);

		}

		// sequential
		for (Player p : others)
		{
			GraphNode playerBranch = nodeFac.MakeGraphNode(GraphNodeType.Default);
			playerBranch.setDescr(p.getName() + " responds");

			List<Card> topCards = GetCards(2, p, Location.DeckTop);
			RevealCards(topCards);

			headNode.setParent(thiefMainNode);
			thiefMainNode.AddChild(headNode);
		}

		thiefMainNode.setParent(currNode);
		currNode.AddChild(thiefMainNode);
	}

	private List<Card> GetReactionCards(Player p)
	{
		// TODO Auto-generated method stub
		return null;
	}

	public void Moat()
	{
		Player self = SelectSelfPlayer();
		DrawCards(self, 2);

	}

}
